<?php
/**
 * if_mysqli驱动
 *
 * @package if
 * @subpackage module.db.drivers
 * @since 1.0
 */
class IDbMysqliDriver extends IDbDriver {
	protected $dataTypes = array(
		"STRING" => IDb::TYPE_STR,
		"CHAR" => IDb::TYPE_STR,
		"VARCHAR" => IDb::TYPE_STR,
		"TINYBLOB" => IDb::TYPE_STR, 
		"TINYTEXT" => IDb::TYPE_STR, 
		"ENUM" => IDb::TYPE_STR, 
		"SET" => IDb::TYPE_STR, 
		"TEXT" => IDb::TYPE_LOB,
		"LONGTEXT" => IDb::TYPE_LOB, 
		"MEDIUMTEXT" => IDb::TYPE_LOB,
		"IMAGE" => IDb::TYPE_LOB,
		"LONGBLOB" => IDb::TYPE_LOB, 
		"BLOB" => IDb::TYPE_LOB,
		"MEDIUMBLOB" => IDb::TYPE_LOB,
		"BINARY" => IDb::TYPE_LOB,
		"YEAR" => IDb::TYPE_DATE,
		"DATE" => IDb::TYPE_DATE,		
		"TIME" => IDb::TYPE_TIME,
		"DATETIME" => IDb::TYPE_TIMESTAMP,
		"TIMESTAMP" => IDb::TYPE_TIMESTAMP,		
		"INT" => IDb::TYPE_INT, 
		"INTEGER" => IDb::TYPE_INT,
		"BIGINT" => IDb::TYPE_LONG,
		"TINYINT" => IDb::TYPE_INT,
		"MEDIUMINT" => IDb::TYPE_INT,
		"SMALLINT" => IDb::TYPE_INT
	);
	
	protected $defaultDataType = IDb::TYPE_FLOAT;
	
	protected $startQuote = "`";
	protected $endQuote = "`";
	
	/**
	 * Enter description here...
	 *
	 * @var mysqli
	 */
	private $mysqli;
	
	function __construct(array $config) {
		parent::__construct($config);
		if (!extension_loaded("mysqli")) {
			throw new IDbException("mysqli extension not be loaded", 0x100077);
		}
	}	
	
	function tableList() {
		$result = $this->nativeExec("SHOW TABLES");
		$array = array();
		while ($row = $this->fetch($result, IDb::FETCH_NUM)) {
			$array[] = $row[0];
		}
		return $array;
	}
	
	function describeTable($name) {
		$array = $this->fetchAll($this->nativeExec("SHOW COLUMNS FROM `{$name}`"), IDb::FETCH_NUM);
		$fields = array();
		foreach ($array as $row) {
			$fld = new stdClass();
			$fld->name = $row[0];
			$type = $row[1];
			
			// split type into type(length):
			$fld->scale = null;
			if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
				$fld->type = $query_array[1];
				$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
				$fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
			} elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
				$fld->type = $query_array[1];
				$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
			} elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
				$fld->type = $query_array[1];
				$arr = explode(",",$query_array[2]);
				$fld->enums = $arr;
				$zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
				$fld->max_length = ($zlen > 0) ? $zlen : 1;
			} else {
				$fld->type = $type;
				$fld->max_length = -1;
			}
			$fld->not_null = ($row[2] != 'YES');
			$fld->primary_key = ($row[3] == 'PRI');
			$fld->auto_increment = (strpos($row[5], 'auto_increment') !== false);
			$fld->binary = (strpos($type,'blob') !== false || strpos($type,'binary') !== false);
			$fld->unsigned = (strpos($type,'unsigned') !== false);
			$fld->zerofill = (strpos($type,'zerofill') !== false);

			if (!$fld->binary) {
				$d = $row[4];
				if ($d != '' && $d != 'NULL') {
					$fld->has_default = true;
					$fld->default_value = $d;
				} else {
					$fld->has_default = false;
				}
			}
			
			$fld->data_type = $this->dataType($fld);
			$fields[$fld->name] = get_object_vars($fld);
		}
		return $fields;		
	}
	
	function nConnect() {
		$this->mysqli = @mysqli_connect($this->config["host"], $this->config["username"], $this->config["password"], $this->config["dbname"], $this->config("port"), $this->config("socket"));
		if (!$this->mysqli) {
			throw new IDbException(mysqli_connect_error(), mysqli_connect_errno());
		}
		
		if ($this->config("charset")) {
			$this->mysqli->query("SET NAMES '" . $this->config["charset"] . "'");
		}
		return $this->mysqli;
	}
	
	function nClose() {
		$this->mysqli->close();
	}
	
	function lastInsertId($name = null) {
		return $this->mysqli->insert_id;
	}
	
	function beginTransaction() {
		$this->mysqli->autocommit(false);
	}
	
	function commit() {
		$this->mysqli->commit();
		$this->mysqli->autocommit(true);
	}
	
	function rollBack() {
		$this->mysqli->rollback();
		$this->mysqli->autocommit(true);
	}
	
	function nQuote($data) {
		if (is_int($data) || is_float($data)) {
			return $data;
		}
		if(is_null($data)) {
			return "NULL";
		}
		return "'" . $this->mysqli->real_escape_string($data) . "'";
	}
	
	function exec($sql) {
		$this->_log("execute:{$sql}");
		$this->mysqli->query($sql);
		$this->_findError();
		return $this->mysqli->affected_rows;
	}
	
	function nativeExec($sql) {
		$this->mysqli = $this->connect();
		$result = $this->mysqli->query($sql);
		$this->_findError();
		return $result;
	}
	
	/**
	 * Enter description here...
	 *
	 * @param MySQLi_Result $result
	 * @param unknown_type $fetchType
	 * @param unknown_type $columnIndex
	 */
	function fetch($result, $fetchType = IDb::FETCH_BOTH, $columnIndex = 0) {
		switch ($fetchType) {
			case IDb::FETCH_ASSOC:
				return $result->fetch_assoc();
			case IDb::FETCH_BOTH:
				return $result->fetch_array();
			case IDb::FETCH_NUM:
				return $result->fetch_row();
			case IDb::FETCH_OBJ:
				return $result->fetch_object();
			case IDb::FETCH_COLUMN:
				$row = $result->fetch_row();
				if (!empty($row) && array_key_exists($columnIndex, $row)) {
					return $row[$columnIndex];
				}
		}
		return null;
	}
	
	function error() {
		$error = $this->mysqli->error;
		if (!$error) {
			return array();
		}
		return array(
			"code" => $this->mysqli->errno,
			"message" => $error
		);
	}
	
	private function _findError() {
		$error = $this->error();
		if (!empty($error)) {
			throw new IDbException($error["message"], $error["code"]);
		}
	}
	
	function rowCount($result) {
		return is_resource($result) ? $result->num_rows : $this->mysqli->affected_rows;
	}
	
	function nLimit($sql, $count, $offset = 0) {
		return $sql . (($offset > 0) ?  " LIMIT {$offset},{$count}" : " LIMIT {$count}");
	}
	
	function move($result, $offset) {
		return $result->data_seek($offset);
	}	
}

?>